package jadeCW;


import jade.core.AID;
import jade.core.Agent;
import jade.core.behaviours.Behaviour;
import jade.core.behaviours.OneShotBehaviour;
import jade.domain.DFService;
import jade.domain.FIPAException;
import jade.domain.FIPAAgentManagement.DFAgentDescription;
import jade.domain.FIPAAgentManagement.ServiceDescription;
import jade.lang.acl.ACLMessage;
import jade.lang.acl.MessageTemplate;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;

public class PatientAgent extends Agent {

	private AID allocatingAgent;
	private int appointment = -1; // -1 --> appointment has not been allocated yet for this patient
	private ArrayList<Integer> preferences;
	private String preference;

	protected void setup() {

		Object[] args = getArguments();

		if (args != null && args.length > 0) {
			preference = (String) args[0];
		}
		else if (args != null && args.length == 0) {
			// agent is indifferent to preferences
		}
		else {
			System.out.println("Need to input args for appointment preferences");

		}
		System.out.println("Patient-Agent " + getAID().getName() + " is ready.");

		addBehaviour(new GetHospitalAppointment());
		addBehaviour(new RequestAppointment());

	}

	protected void takeDown() {

		System.out.println("Buyer-Agent " + getAID().getName() + " terminating.");
	}

	private class GetHospitalAppointment extends OneShotBehaviour {

		@Override
		public void action() {

			DFAgentDescription template = new DFAgentDescription();
			ServiceDescription sd = new ServiceDescription();
			sd.setType("allocate-appointments");
			template.addServices(sd);
			try {
				DFAgentDescription[] results = DFService.search(myAgent, template);
				// Only 1 hospital agent for this assignment
				allocatingAgent = results[0].getName();
			}
			catch (FIPAException e) {
				e.printStackTrace();
			}

		}

	}

	private class RequestAppointment extends Behaviour {

		private MessageTemplate messageTemplate;
		private int step = 0;

		@Override
		public void action() {

			switch (step) {
			case (0):
				// 1. check an agent is known to provide allocate-appointments service
				// 2. Check that the agent has not already been allocated an appointement (-1)
				// 3. request and appointment from an agent that provides the "allocate-appointments service"
				if (allocatingAgent != null) {
					if (appointment == -1) {
						ACLMessage cfp = new ACLMessage(ACLMessage.CFP);
						cfp.addReceiver(allocatingAgent);
						cfp.setConversationId("allocate-appointments");
						cfp.setReplyWith("cfp" + System.currentTimeMillis());
						myAgent.send(cfp);
						messageTemplate = MessageTemplate.and(MessageTemplate.MatchConversationId("allocate-appointments"), MessageTemplate.MatchInReplyTo(cfp.getReplyWith()));
					}
				}
				step = 1;
				break;

			// 4. receive response whether service has been allocated or not
			// 5. in case an appointment is allocated in the response, modify the parent's agent's state so that it knows what appointment is has been allocated
			case (1):
				ACLMessage reply = myAgent.receive(messageTemplate);
				if (reply != null) {
					if (reply.getPerformative() == ACLMessage.AGREE) {

						appointment = Integer.parseInt(reply.getContent());
					}
					else if (reply.getPerformative() == ACLMessage.REFUSE) {
						// TODO do sth or is this redundant?
					}

				}
				else {
					block();
				}
				break;

			}

		}

		@Override
		public boolean done() {

			// TODO Auto-generated method stub
			return false;
		}

	}

	private class FindAppointmentOwner extends Behaviour {

		private MessageTemplate messageTemplate;
		private int step = 0;
		private boolean appointmentHappy = false;
		ArrayList<Integer> prefs;
		int prefIndex = 0;
		int i = 0;

		@Override
		public void action() {

			// 1. if there is an appointment more preferred than the current one select that one
			switch (step) {
			case (0):
				if (i < preference.length()) { // start at index i and get the next preferences
					prefs = getNextPreferences(preference);
					Iterator<Integer> it = prefs.iterator();
					while (it.hasNext()) {
						int request = it.next();
						if (request == appointment) {
							appointmentHappy = true; // currentAppointment is preference so all good
							break;
						}
					}
					step = 1; // request a new appointment
				}
				break;

			// 2. query the agent with preference
			case (1):
				ACLMessage cfp = new ACLMessage(ACLMessage.CFP);
				cfp.addReceiver(allocatingAgent);
				cfp.setConversationId("allocate-appointments");
				cfp.setReplyWith("cfp" + System.currentTimeMillis());
				try {
					cfp.setContentObject(prefs.get(prefIndex));
				}
				catch (IOException e) {
					e.printStackTrace();
				}
				myAgent.send(cfp);
				messageTemplate = MessageTemplate.and(MessageTemplate.MatchConversationId("allocate-appointments"), MessageTemplate.MatchInReplyTo(cfp.getReplyWith()));

				step = 2;
				break;

			// 3.receive the response

			case (2):
				ACLMessage reply = myAgent.receive(messageTemplate);
				if (reply != null) {
					if (reply.getPerformative() == ACLMessage.AGREE) {

						appointment = Integer.parseInt(reply.getContent());
						appointmentHappy = true;
					}
					else if (reply.getPerformative() == ACLMessage.REFUSE) {
						if ((prefIndex + 1) < prefs.size()) {
							prefIndex++; // send out requests for preferences that are equally preferred
							step = 1;
						}
						else {
							step = 0; // go back to step 0 and check if there are more preferences
						}
					}

				}
				break;

			}
		}

		@Override
		public boolean done() {

			return appointmentHappy == true;
		}

		private ArrayList<Integer> getNextPreferences(String arg) {

			ArrayList<Integer> preferences = new ArrayList<Integer>();
			String no = "";
			while (arg.charAt(i) != '-') {
				if (Character.isDigit(arg.charAt(i))) {
					no += arg.charAt(i);
					// check if 2nd digit, i.e. all numbers > 9
					if (Character.isDigit(arg.charAt(i + 1))) {
						no += arg.charAt(i + 1);
						i++;
					}
					preferences.add(Integer.parseInt(no));
					no = "";
				}
				i++;
			}

			return preferences;
		}

	}
}
